1 About File

  1. You are supposed to submit an R Notebook File with a file name a2_YourID.nb.html.
  • Some submitted an HTML file, such as a2_YourID.html. You need to create an R Notebook. Use the template in Moodle. It creates a file with *.nb.html at the end automatically.
  • Some did not run each code chunk. You should run each code or select ‘Run all’ under ‘Run’ button. If some code chunk has a problem or an error, run each code chunk or use Run all chunk above or Run all chunk below, so the result appear in your R Notebook file.
  1. You are supposed to write observations.
  • Writing codes seem to be challenging, however, we are learning ‘data analysis’ not ‘programming’. Do not forget to write explanations of the data, questions and observations.
  1. Cheat Sheets, Posit Primers, and the textbook ‘R for Data Science’ are the first set of references you should look at together wih my lecture materials.

2 Set up

library(tidyverse)
library(gapminder)

The following (df <- gapminder) is a short-hand of

df <- gapminder
df
(df <- gapminder)

3 General Comments

3.1 Varibles

We should know first about the variables. At least you must know if each of the variables is a categorical variable or a numerical variable.

For example, in the gapminder data, country, continent are categorical variables, and year, lifeExp, pop, gdpPercap are numerical variables. It is possible to treat year as a categorical variable.

3.2 Example: datasets::CO2

3.2.1 The first step

You can obtain basic information of the data by the following or typing CO2 in the search box under Help tab. You can see the same at: https://stat.ethz.ch/R-manual/R-devel/library/datasets/html/00Index.html

help(CO2) # or ? CO2
  • Description: The CO2 data frame has 84 rows and 5 columns of data from an experiment on the cold tolerance of the grass species Echinochloa crus-galli.
  • Usage: CO2
  • Format
    • An object of class c(“nfnGroupedData”, “nfGroupedData”, “groupedData”, “data.frame”) containing the following columns:

    • Plant: an ordered factor with levels Qn1 < Qn2 < Qn3 < … < Mc1 giving a unique identifier for each plant.

    • Type: a factor with levels Quebec Mississippi giving the origin of the plant

    • Treatment: a factor with levels nonchilled chilled

    • conc: a numeric vector of ambient carbon dioxide concentrations (mL/L).

    • uptake: a numeric vector of carbon dioxide uptake rates (/m^2μmol/m 2 sec).

  • Details: The CO_2 uptake of six plants from Quebec and six plants from Mississippi was measured at several levels of ambient CO_2 concentration. Half the plants of each type were chilled overnight before the experiment was conducted.
    • This dataset was originally part of package nlme, and that has methods (including for [, as.data.frame, plot and print) for its grouped-data classes.
  • Source: Potvin, C., Lechowicz, M. J. and Tardif, S. (1990) “The statistical analysis of ecophysiological response curves obtained from experiments involving repeated measures”, Ecology, 71, 1389–1400.
    • Pinheiro, J. C. and Bates, D. M. (2000) Mixed-effects Models in S and S-PLUS, Springer.
df_co2 <- as_tibble(datasets::CO2) # what happens if simply `df_co2 <- datasets::CO2`
df_co2

You can use head(CO2) if you set df_co2 <-CO2 or df_co2 <- datasets::CO2.

glimpse(df_co2)
Rows: 84
Columns: 5
$ Plant     <ord> Qn1, Qn1, Qn1, Qn1, Qn1, Qn1, Qn1, Qn2, Qn2, Qn2, Qn2, Qn2, Qn2, Qn2, Qn3, Qn…
$ Type      <fct> Quebec, Quebec, Quebec, Quebec, Quebec, Quebec, Quebec, Quebec, Quebec, Quebe…
$ Treatment <fct> nonchilled, nonchilled, nonchilled, nonchilled, nonchilled, nonchilled, nonch…
$ conc      <dbl> 95, 175, 250, 350, 500, 675, 1000, 95, 175, 250, 350, 500, 675, 1000, 95, 175…
$ uptake    <dbl> 16.0, 30.4, 34.8, 37.2, 35.3, 39.2, 39.7, 13.6, 27.3, 37.1, 41.8, 40.6, 41.4,…

“factor” is a categorical data, and “double” is a numerical data.

class(df_co2$Plant)
[1] "ordered" "factor" 
class(df_co2$Type)
[1] "factor"
class(df_co2$Treatment)
[1] "factor"
class(df_co2$conc)
[1] "numeric"
class(df_co2$uptake)
[1] "numeric"
summary(df_co2)
     Plant             Type         Treatment       conc          uptake     
 Qn1    : 7   Quebec     :42   nonchilled:42   Min.   :  95   Min.   : 7.70  
 Qn2    : 7   Mississippi:42   chilled   :42   1st Qu.: 175   1st Qu.:17.90  
 Qn3    : 7                                    Median : 350   Median :28.30  
 Qc1    : 7                                    Mean   : 435   Mean   :27.21  
 Qc3    : 7                                    3rd Qu.: 675   3rd Qu.:37.12  
 Qc2    : 7                                    Max.   :1000   Max.   :45.50  
 (Other):42                                                                  

3.2.2 Try as many visualizations as possible

Then you can choose appropriate ones later in your research.

3.2.2.1 Histogram

df_co2 %>% ggplot(aes(x = conc)) + geom_histogram()

df_co2 %>% ggplot(aes(x = uptake)) + geom_histogram()

3.2.2.2 Box Plots

df_co2 %>% ggplot(aes(x = factor(conc), y = uptake)) + geom_boxplot()

df_co2 %>% ggplot(aes(x = factor(conc), y = uptake, fill = Type)) + geom_boxplot()

df_co2 %>% ggplot(aes(x = factor(conc), y = uptake, fill = Treatment)) + geom_boxplot()

df_co2 %>% ggplot(aes(x = Plant, y = uptake, fill = Treatment)) + geom_boxplot()

df_co2 %>% ggplot(aes(x = Plant, y = uptake, fill = Type)) + geom_boxplot()

df_co2 %>% ggplot(aes(x = Plant, y = uptake, fill = Type, color = Treatment)) + geom_boxplot()

What can you see? Write your observations.

3.2.2.3 Scatter Plots

df_co2 %>% ggplot(aes(x = conc, y = uptake, color = Treatment)) + geom_point()

df_co2 %>% ggplot(aes(x = Plant, y = Type, color = Treatment, size = conc)) + geom_point()

df_co2 %>% ggplot(aes(x = Plant, y = Type, size = conc, shape = Treatment)) + geom_point() + facet_wrap(vars(Treatment))

ggplot(data = df_co2) + 
  geom_point(aes(x = conc, y = uptake))

The following prints a vector.

df_co2 %>% distinct(conc) %>% pull()
[1]   95  175  250  350  500  675 1000

The following code generates a data frame.

df_co2 %>% distinct(conc)
ggplot(data = CO2) + 
  geom_line(aes(x = conc, y = uptake))

The code above did not work, and the line graph is not appropriate in this case. There are so many update values at the same conc.

3.2.3 Example. datasets::Seatbelts

Search the data information.

Road Casualties in Great Britain 1969–84

  • Seatbelts is a multiple time series, with columns
  • DriversKilled: car drivers killed.
  • drivers: same as UKDriverDeaths.
  • front: front-seat passengers killed or seriously injured.
  • rear: rear-seat passengers killed or seriously injured.
  • kms: distance driven.
  • PetrolPrice: petrol price.
  • VanKilled: number of van (‘light goods vehicle’) drivers.
  • law: 0/1: was the law in effect that month?

References Harvey, A. C. and Durbin, J. (1986). The effects of seat belt legislation on British road casualties: A case study in structural time series modelling. Journal of the Royal Statistical Society series A, 149, 187–227. doi:10.2307/2981553.

The paper is available as you log-in to ICU Library > E-Databases > JSTOR

Can you see the difference of the following two codes?

head(Seatbelts)
     DriversKilled drivers front rear   kms PetrolPrice VanKilled law
[1,]           107    1687   867  269  9059   0.1029718        12   0
[2,]            97    1508   825  265  7685   0.1023630         6   0
[3,]           102    1507   806  319  9963   0.1020625        12   0
[4,]            87    1385   814  407 10955   0.1008733         8   0
[5,]           119    1632   991  454 11823   0.1010197        10   0
[6,]           106    1511   945  427 12391   0.1005812        13   0
df_sb <- as_tibble(datasets::Seatbelts)
df_sb
summary(df_sb)
 DriversKilled      drivers         front             rear            kms       
 Min.   : 60.0   Min.   :1057   Min.   : 426.0   Min.   :224.0   Min.   : 7685  
 1st Qu.:104.8   1st Qu.:1462   1st Qu.: 715.5   1st Qu.:344.8   1st Qu.:12685  
 Median :118.5   Median :1631   Median : 828.5   Median :401.5   Median :14987  
 Mean   :122.8   Mean   :1670   Mean   : 837.2   Mean   :401.2   Mean   :14994  
 3rd Qu.:138.0   3rd Qu.:1851   3rd Qu.: 950.8   3rd Qu.:456.2   3rd Qu.:17202  
 Max.   :198.0   Max.   :2654   Max.   :1299.0   Max.   :646.0   Max.   :21626  
  PetrolPrice        VanKilled           law        
 Min.   :0.08118   Min.   : 2.000   Min.   :0.0000  
 1st Qu.:0.09258   1st Qu.: 6.000   1st Qu.:0.0000  
 Median :0.10448   Median : 8.000   Median :0.0000  
 Mean   :0.10362   Mean   : 9.057   Mean   :0.1198  
 3rd Qu.:0.11406   3rd Qu.:12.000   3rd Qu.:0.0000  
 Max.   :0.13303   Max.   :17.000   Max.   :1.0000  

Which visualization do you apply?

df_sb %>% ggplot(aes(x = factor(law), y = DriversKilled)) + geom_boxplot()

What do you observe above?

df_sb %>% ggplot(aes(x = PetrolPrice, y = DriversKilled)) + geom_point() +
  geom_smooth(formula = y~x, method = "lm", se = FALSE)

What can you see above?

df_sb %>% ggplot(aes(x = kms, y = DriversKilled)) + geom_point() +
  geom_smooth(formula = y~x, method = "lm", se = FALSE)

What can you see above?

We will learn how to use pivot_longer and pivot_wider in EDA4.

df_sb %>% 
  pivot_longer(cols = 2:4, names_to = "seat", values_to = "value")
df_sb %>% 
  pivot_longer(cols = 2:4, names_to = "seat", values_to = "value") %>%
  ggplot() + geom_boxplot(aes(x = seat, y = value, fill = seat))

What can you observe?

df %>% filter(continent == "Asia") %>%
  ggplot(aes(x = year, y = gdpPercap, color = country)) + geom_line()

Appropriate graph?

3.3 Gapminder

ggplot(df, aes(x = continent)) + geom_bar()

ggplot(df, aes(x = continent, fill = continent)) + geom_bar(width = 0.75)

ggplot(df, aes(x = continent, y = pop)) + geom_boxplot()

ggplot(df, aes(x = continent, y = log10(pop))) + geom_boxplot()

Alternately, you can use coord_trans(x = "identity", y = "log10") in stead of y = log10(pop). Can you see the difference?

ggplot(df, aes(x = continent, y = pop)) + geom_boxplot() + 
  coord_trans(x = "identity", y = "log10")

df %>% filter(year %in% c(1957, 1982, 2007)) %>%
  ggplot() + geom_boxplot(aes(x = continent, y = pop, fill = factor(year))) +
    coord_trans(x = "identity", y = "log10")

df %>% filter(year %in% c(1957, 1982, 2007)) %>%
  ggplot() + geom_boxplot(aes(x = continent, y = gdpPercap, fill = factor(year))) +
  coord_trans(x = "identity", y = "log10")

ggplot(df, aes(gdpPercap, lifeExp)) + geom_point(aes(color=continent))

df %>% ggplot(aes(gdpPercap, lifeExp, color = continent)) + geom_point() +
  coord_trans(x = "log10", y = "identity")

df %>% filter(year %in% c(2007)) %>%
  ggplot() + geom_point(aes(x = pop, y = gdpPercap, col = continent)) +
  coord_trans(x = "log10", y = "log10")

df %>% filter(year %in% c(1957)) %>%
  ggplot() + geom_point(aes(x = gdpPercap, y = lifeExp, col = continent, size = pop)) +
  coord_trans(x = "log10", y = "identity")

df %>% filter(year %in% c(2007)) %>%
  ggplot() + geom_point(aes(x = gdpPercap, y = lifeExp, col = continent, size = pop)) +
  coord_trans(x = "log10", y = "identity")

df %>% filter(year %in% c(1957, 2007)) %>%
  ggplot() + geom_point(aes(x = gdpPercap, y = lifeExp, col = continent, size = pop)) +
  coord_trans(x = "log10", y = "identity") +
  facet_wrap(vars(year))

df_lifeExp <- df %>% group_by(continent, year) %>% 
  summarize(mean_lifeExp = mean(lifeExp), median_lifeExp = median(lifeExp), max_lifeExp = max(lifeExp), min_lifeExp = min(lifeExp))
`summarise()` has grouped output by 'continent'. You can override using the `.groups` argument.

The code above gives a message, but it works.

df_lifeExp %>% ggplot(aes(x = year, y = median_lifeExp, color = continent)) +
  geom_line()

If you do not want to have a message, the following is an option. Otherwise, grouping is kept and you can get the original data back by ungroup().

df_pop <- df %>% group_by(continent, year) %>% 
  summarize(mean_pop = mean(pop), median_pop = median(pop), max_pop = max(pop), min_pop = min(pop), .groups = "drop")
df_pop %>% ggplot(aes(x = year, y = mean_pop, color = continent)) +
  geom_line()

The following two codes create the same chart.

df_pop %>% ggplot(aes(x = year, y = mean_pop, color = continent, linetype = continent)) +
  geom_line()

df_pop %>% ggplot() +
  geom_line(aes(x = year, y = mean_pop, color = continent)) + 
  geom_line(aes(x = year, y = median_pop, linetype = continent))

4 Responses to Questions

4.1 Q1. Two categorical variables and one numerical variables

Eg. Smoking, Alcohol and (O)esophageal Cancer

(df_esoph <- as_tibble(esoph))

df_esoph has three categorical variables and one numerical variable ncases to investigate.

Comments: I wanted to include three variables in the first exercise to be able to compare tobacco consumption, number of cases of cancer, and age in the same graph but I was not able to do it.

Solutions: There are various ways you can choose from.

Scatter plot with size by geom_point().

ggplot(df_esoph) + geom_point(aes(agegp, tobgp, size=ncases))

Heatmap with geom_tile()

ggplot(df_esoph) + geom_tile(mapping = aes(x = agegp, y = tobgp, fill = ncases))

geom_boxplot()

ggplot(df_esoph, aes(x= tobgp, y=ncases, fill=agegp))+geom_boxplot()

ggplot(df_esoph, aes(x= agegp, y=ncases, fill=tobgp))+geom_boxplot()

geom_col()

ggplot(df_esoph, aes(x= agegp, y=ncases, fill=tobgp))+geom_col()

Default position is “stack”.

ggplot(df_esoph, aes(x= agegp, y=ncases, fill=tobgp))+geom_col(position = "dodge")

4.2 Q2. Combine two charts

df %>%
  select(country, continent, year, gdpPercap) %>%
  filter(continent %in% c("Asia", "Europe")) %>%
  group_by(continent, year) %>%
  summarise(mean_GDPperCapita = mean(gdpPercap)) %>%
  ggplot(aes(x=year)) +
  geom_line(aes(y=mean_GDPperCapita, color=continent)) +
  ggtitle("GDP oer capita by continents, 1950's to today") 
`summarise()` has grouped output by 'continent'. You can override using the `.groups` argument.

df %>% 
  select(country, year, gdpPercap) %>% 
  filter(country %in% c("Israel", "Japan", "Norway", "China", "Ireland")) %>% 
  ggplot(aes(x=year)) + 
  geom_line(aes(y=gdpPercap, color=country))

Question. I have not managed to add on the same graph of the continents the data for the individual countries, as I would have liked:

Solution. Construct two data sets and combine them into one.

ggplot2 starts with one data.

df_2c <- df %>%
  select(continent, year, gdpPercap) %>%
  filter(continent %in% c("Asia", "Europe")) %>%
  group_by(continent, year) %>%
  summarise(gdpPercap = mean(gdpPercap), .groups = 'drop') %>%
  select(country = continent, year, gdpPercap)

df_5c <- df %>%
  select(country, year, gdpPercap) %>%
  filter(country %in% c("Israel", "Japan", "Norway", "China", "Ireland"))

df_2c %>% bind_rows(df_5c) %>%
  ggplot(aes(x = year, y = gdpPercap, color = country)) + geom_line()

Use mutate.

df %>%
  group_by(continent, year) %>%
  mutate(mean_by_continent = mean(gdpPercap)) %>%
  ungroup() %>%
  filter(country %in% c("Israel", "Japan", "Norway", "China", "Ireland")) %>%
  ggplot(aes(x = year)) +
    geom_line(aes(y = gdpPercap, color=country)) +
    geom_line(aes(y = mean_by_continent, linetype=continent)) +
    labs(title = "GDP oer capita of five countries", subtitle = "Mean of GDP per capita of their continent") 

When you want to change the linetype manually, use scale_linetype_manual().

df %>%
  group_by(continent, year) %>%
  mutate(mean_by_continent = mean(gdpPercap)) %>%
  ungroup() %>%
  filter(country %in% c("Israel", "Japan", "Norway", "China", "Ireland")) %>%
  ggplot(aes(x = year)) +
    geom_line(aes(y = gdpPercap, color=country)) +
    geom_line(aes(y = mean_by_continent, linetype=continent)) + 
    scale_linetype_manual(values = c("Asia" = "twodash", "Europe" = "longdash")) + 
    labs(title = "GDP oer capita of five countries", subtitle = "Mean of GDP per capita of their continent") 

5 Appendix: Change colors, shapes, linetypes, etc. manually

Example: Default

df %>%
  filter(country %in% c("Germany", "Japan", "United States")) %>%
  ggplot() +
    geom_line(aes(x = year, y = gdpPercap, color=country, linetype=continent))

df %>%
  filter(country %in% c("Germany", "Japan", "United States")) %>%
  ggplot(aes(x = year, y = gdpPercap)) +
    geom_line(aes(color=country, linetype=continent)) +
    geom_point(aes(shape = country)) +
    scale_colour_manual(values = scales::hue_pal(direction = -1)(3)) +
    scale_linetype_manual(values = c("Europe" = "dotted", "Asia" = "dotdash", "Americas" = "longdash")) +
    scale_shape_manual(values = c("Germany" = 7, "Japan" = 9, "United States" = 12))

LS0tCnRpdGxlOiAiUmVzcG9uc2VzIHRvIEFzc2lnbm1lbnQgMiIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGhpZ2hsaWdodDogaGFkZG9jawogICAgdGhlbWU6IGNvc21vCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwotLS0KCiMgQWJvdXQgRmlsZQoKMS4gWW91IGFyZSBzdXBwb3NlZCB0byBzdWJtaXQgYW4gUiBOb3RlYm9vayBGaWxlIHdpdGggYSBmaWxlIG5hbWUgYTJfWW91cklELm5iLmh0bWwuCiAgLSBTb21lIHN1Ym1pdHRlZCBhbiBIVE1MIGZpbGUsIHN1Y2ggYXMgYTJfWW91cklELmh0bWwuIFlvdSBuZWVkIHRvIGNyZWF0ZSBhbiBSIE5vdGVib29rLiBVc2UgdGhlIHRlbXBsYXRlIGluIE1vb2RsZS4gSXQgY3JlYXRlcyBhIGZpbGUgd2l0aCAqLm5iLmh0bWwgYXQgdGhlIGVuZCBhdXRvbWF0aWNhbGx5LgogIC0gU29tZSBkaWQgbm90IHJ1biBlYWNoIGNvZGUgY2h1bmsuIFlvdSBzaG91bGQgcnVuIGVhY2ggY29kZSBvciBzZWxlY3QgJ1J1biBhbGwnIHVuZGVyICdSdW4nIGJ1dHRvbi4gSWYgc29tZSBjb2RlIGNodW5rIGhhcyBhIHByb2JsZW0gb3IgYW4gZXJyb3IsIHJ1biBlYWNoIGNvZGUgY2h1bmsgb3IgdXNlIFJ1biBhbGwgY2h1bmsgYWJvdmUgb3IgUnVuIGFsbCBjaHVuayBiZWxvdywgc28gdGhlIHJlc3VsdCBhcHBlYXIgaW4geW91ciBSIE5vdGVib29rIGZpbGUuCiAgCjIuIFlvdSBhcmUgc3VwcG9zZWQgdG8gd3JpdGUgb2JzZXJ2YXRpb25zLiAKICAtIFdyaXRpbmcgY29kZXMgc2VlbSB0byBiZSBjaGFsbGVuZ2luZywgaG93ZXZlciwgd2UgYXJlIGxlYXJuaW5nICdkYXRhIGFuYWx5c2lzJyBub3QgJ3Byb2dyYW1taW5nJy4gRG8gbm90IGZvcmdldCB0byB3cml0ZSBleHBsYW5hdGlvbnMgb2YgdGhlIGRhdGEsIHF1ZXN0aW9ucyBhbmQgb2JzZXJ2YXRpb25zLgogIAozLiBDaGVhdCBTaGVldHMsIFBvc2l0IFByaW1lcnMsIGFuZCB0aGUgdGV4dGJvb2sgJ1IgZm9yIERhdGEgU2NpZW5jZScgYXJlIHRoZSBmaXJzdCBzZXQgb2YgcmVmZXJlbmNlcyB5b3Ugc2hvdWxkIGxvb2sgYXQgdG9nZXRoZXIgd2loIG15IGxlY3R1cmUgbWF0ZXJpYWxzLgoKIyBTZXQgdXAKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnYXBtaW5kZXIpCmBgYAoKVGhlIGZvbGxvd2luZyBgKGRmIDwtIGdhcG1pbmRlcilgIGlzIGEgc2hvcnQtaGFuZCBvZiAKCmBgYApkZiA8LSBnYXBtaW5kZXIKZGYKYGBgCgpgYGB7cn0KKGRmIDwtIGdhcG1pbmRlcikKYGBgCgojIEdlbmVyYWwgQ29tbWVudHMKCiMjIFZhcmlibGVzCgpXZSBzaG91bGQga25vdyBmaXJzdCBhYm91dCB0aGUgdmFyaWFibGVzLiBBdCBsZWFzdCB5b3UgbXVzdCBrbm93IGlmIGVhY2ggb2YgdGhlIHZhcmlhYmxlcyBpcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIG9yIGEgbnVtZXJpY2FsIHZhcmlhYmxlLgoKRm9yIGV4YW1wbGUsIGluIHRoZSBgZ2FwbWluZGVyYCBkYXRhLCBgY291bnRyeWAsIGBjb250aW5lbnRgIGFyZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIGFuZCBgeWVhcmAsIGBsaWZlRXhwYCwgYHBvcGAsIGBnZHBQZXJjYXBgIGFyZSBudW1lcmljYWwgdmFyaWFibGVzLiBJdCBpcyBwb3NzaWJsZSB0byB0cmVhdCBgeWVhcmAgYXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gCgojIyBFeGFtcGxlOiBgZGF0YXNldHM6OkNPMmAKCiMjIyBUaGUgZmlyc3Qgc3RlcAoKWW91IGNhbiBvYnRhaW4gYmFzaWMgaW5mb3JtYXRpb24gb2YgdGhlIGRhdGEgYnkgdGhlIGZvbGxvd2luZyBvciB0eXBpbmcgQ08yIGluIHRoZSBzZWFyY2ggYm94IHVuZGVyIEhlbHAgdGFiLiBZb3UgY2FuIHNlZSB0aGUgc2FtZSBhdDogaHR0cHM6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2RhdGFzZXRzL2h0bWwvMDBJbmRleC5odG1sCgpgYGB7ciBldmFsPUZBTFNFfQpoZWxwKENPMikgIyBvciA/IENPMgpgYGAKCiogKipEZXNjcmlwdGlvbioqOiBUaGUgQ08yIGRhdGEgZnJhbWUgaGFzIDg0IHJvd3MgYW5kIDUgY29sdW1ucyBvZiBkYXRhIGZyb20gYW4gZXhwZXJpbWVudCBvbiB0aGUgY29sZCB0b2xlcmFuY2Ugb2YgdGhlIGdyYXNzIHNwZWNpZXMgRWNoaW5vY2hsb2EgY3J1cy1nYWxsaS4KKiAqKlVzYWdlKio6IENPMgoqICoqRm9ybWF0KioKICAtIEFuIG9iamVjdCBvZiBjbGFzcyBjKCJuZm5Hcm91cGVkRGF0YSIsICJuZkdyb3VwZWREYXRhIiwgImdyb3VwZWREYXRhIiwgImRhdGEuZnJhbWUiKSBjb250YWluaW5nIHRoZSBmb2xsb3dpbmcgY29sdW1uczoKCiAgLSBQbGFudDogYW4gb3JkZXJlZCBmYWN0b3Igd2l0aCBsZXZlbHMgUW4xIDwgUW4yIDwgUW4zIDwgLi4uIDwgTWMxIGdpdmluZyBhIHVuaXF1ZSBpZGVudGlmaWVyIGZvciBlYWNoIHBsYW50LgoKICAtIFR5cGU6IGEgZmFjdG9yIHdpdGggbGV2ZWxzIFF1ZWJlYyBNaXNzaXNzaXBwaSBnaXZpbmcgdGhlIG9yaWdpbiBvZiB0aGUgcGxhbnQKCiAgLSBUcmVhdG1lbnQ6IGEgZmFjdG9yIHdpdGggbGV2ZWxzIG5vbmNoaWxsZWQgY2hpbGxlZAoKICAtIGNvbmM6IGEgbnVtZXJpYyB2ZWN0b3Igb2YgYW1iaWVudCBjYXJib24gZGlveGlkZSBjb25jZW50cmF0aW9ucyAobUwvTCkuCgogIC0gdXB0YWtlOiBhIG51bWVyaWMgdmVjdG9yIG9mIGNhcmJvbiBkaW94aWRlIHVwdGFrZSByYXRlcyAoXG11XG1ib3h7bW9sfS9tXjLOvG1vbC9tIDIKICBzZWMpLgogIAoqICoqRGV0YWlscyoqOiBUaGUgQ09fMiB1cHRha2Ugb2Ygc2l4IHBsYW50cyBmcm9tIFF1ZWJlYyBhbmQgc2l4IHBsYW50cyBmcm9tIE1pc3Npc3NpcHBpIHdhcyBtZWFzdXJlZCBhdCBzZXZlcmFsIGxldmVscyBvZiBhbWJpZW50IENPXzIgY29uY2VudHJhdGlvbi4gSGFsZiB0aGUgcGxhbnRzIG9mIGVhY2ggdHlwZSB3ZXJlIGNoaWxsZWQgb3Zlcm5pZ2h0IGJlZm9yZSB0aGUgZXhwZXJpbWVudCB3YXMgY29uZHVjdGVkLgogIC0gVGhpcyBkYXRhc2V0IHdhcyBvcmlnaW5hbGx5IHBhcnQgb2YgcGFja2FnZSBubG1lLCBhbmQgdGhhdCBoYXMgbWV0aG9kcyAoaW5jbHVkaW5nIGZvciBbLCBhcy5kYXRhLmZyYW1lLCBwbG90IGFuZCBwcmludCkgZm9yIGl0cyBncm91cGVkLWRhdGEgY2xhc3Nlcy4KCiogKipTb3VyY2UqKjogUG90dmluLCBDLiwgTGVjaG93aWN6LCBNLiBKLiBhbmQgVGFyZGlmLCBTLiAoMTk5MCkg4oCcVGhlIHN0YXRpc3RpY2FsIGFuYWx5c2lzIG9mIGVjb3BoeXNpb2xvZ2ljYWwgcmVzcG9uc2UgY3VydmVzIG9idGFpbmVkIGZyb20gZXhwZXJpbWVudHMgaW52b2x2aW5nIHJlcGVhdGVkIG1lYXN1cmVz4oCdLCBFY29sb2d5LCA3MSwgMTM4OeKAkzE0MDAuCiAgLSBQaW5oZWlybywgSi4gQy4gYW5kIEJhdGVzLCBELiBNLiAoMjAwMCkgTWl4ZWQtZWZmZWN0cyBNb2RlbHMgaW4gUyBhbmQgUy1QTFVTLCBTcHJpbmdlci4KCgpgYGB7cn0KZGZfY28yIDwtIGFzX3RpYmJsZShkYXRhc2V0czo6Q08yKSAjIHdoYXQgaGFwcGVucyBpZiBzaW1wbHkgYGRmX2NvMiA8LSBkYXRhc2V0czo6Q08yYApkZl9jbzIKYGBgCgpZb3UgY2FuIHVzZSBgaGVhZChDTzIpYCBpZiB5b3Ugc2V0IGBkZl9jbzIgPC1DTzJgIG9yIGBkZl9jbzIgPC0gZGF0YXNldHM6OkNPMmAuCgpgYGB7cn0KZ2xpbXBzZShkZl9jbzIpCmBgYAoKImZhY3RvciIgaXMgYSBjYXRlZ29yaWNhbCBkYXRhLCBhbmQgImRvdWJsZSIgaXMgYSBudW1lcmljYWwgZGF0YS4KCmBgYHtyfQpjbGFzcyhkZl9jbzIkUGxhbnQpCmNsYXNzKGRmX2NvMiRUeXBlKQpjbGFzcyhkZl9jbzIkVHJlYXRtZW50KQpjbGFzcyhkZl9jbzIkY29uYykKY2xhc3MoZGZfY28yJHVwdGFrZSkKYGBgCmBgYHtyfQpzdW1tYXJ5KGRmX2NvMikKYGBgCgojIyMgVHJ5IGFzIG1hbnkgdmlzdWFsaXphdGlvbnMgYXMgcG9zc2libGUKClRoZW4geW91IGNhbiBjaG9vc2UgYXBwcm9wcmlhdGUgb25lcyBsYXRlciBpbiB5b3VyIHJlc2VhcmNoLgoKIyMjIyBIaXN0b2dyYW0KCgpgYGB7cn0KZGZfY28yICU+JSBnZ3Bsb3QoYWVzKHggPSBjb25jKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKYGBge3J9CmRmX2NvMiAlPiUgZ2dwbG90KGFlcyh4ID0gdXB0YWtlKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKIyMjIyBCb3ggUGxvdHMKCmBgYHtyfQpkZl9jbzIgJT4lIGdncGxvdChhZXMoeCA9IGZhY3Rvcihjb25jKSwgeSA9IHVwdGFrZSkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7cn0KZGZfY28yICU+JSBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IoY29uYyksIHkgPSB1cHRha2UsIGZpbGwgPSBUeXBlKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKYGBge3J9CmRmX2NvMiAlPiUgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKGNvbmMpLCB5ID0gdXB0YWtlLCBmaWxsID0gVHJlYXRtZW50KSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCgpgYGB7cn0KZGZfY28yICU+JSBnZ3Bsb3QoYWVzKHggPSBQbGFudCwgeSA9IHVwdGFrZSwgZmlsbCA9IFRyZWF0bWVudCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgoKYGBge3J9CmRmX2NvMiAlPiUgZ2dwbG90KGFlcyh4ID0gUGxhbnQsIHkgPSB1cHRha2UsIGZpbGwgPSBUeXBlKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCgpgYGB7cn0KZGZfY28yICU+JSBnZ3Bsb3QoYWVzKHggPSBQbGFudCwgeSA9IHVwdGFrZSwgZmlsbCA9IFR5cGUsIGNvbG9yID0gVHJlYXRtZW50KSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCldoYXQgY2FuIHlvdSBzZWU/CldyaXRlIHlvdXIgb2JzZXJ2YXRpb25zLgoKIyMjIyBTY2F0dGVyIFBsb3RzCgpgYGB7cn0KZGZfY28yICU+JSBnZ3Bsb3QoYWVzKHggPSBjb25jLCB5ID0gdXB0YWtlLCBjb2xvciA9IFRyZWF0bWVudCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKCmBgYHtyfQpkZl9jbzIgJT4lIGdncGxvdChhZXMoeCA9IFBsYW50LCB5ID0gVHlwZSwgY29sb3IgPSBUcmVhdG1lbnQsIHNpemUgPSBjb25jKSkgKyBnZW9tX3BvaW50KCkKYGBgCgoKYGBge3J9CmRmX2NvMiAlPiUgZ2dwbG90KGFlcyh4ID0gUGxhbnQsIHkgPSBUeXBlLCBzaXplID0gY29uYywgc2hhcGUgPSBUcmVhdG1lbnQpKSArIGdlb21fcG9pbnQoKSArIGZhY2V0X3dyYXAodmFycyhUcmVhdG1lbnQpKQpgYGAKCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkZl9jbzIpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IGNvbmMsIHkgPSB1cHRha2UpKQpgYGAKClRoZSBmb2xsb3dpbmcgcHJpbnRzIGEgdmVjdG9yLgoKYGBge3J9CmRmX2NvMiAlPiUgZGlzdGluY3QoY29uYykgJT4lIHB1bGwoKQpgYGAKClRoZSBmb2xsb3dpbmcgY29kZSBnZW5lcmF0ZXMgYSBkYXRhIGZyYW1lLgoKYGBge3J9CmRmX2NvMiAlPiUgZGlzdGluY3QoY29uYykKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBDTzIpICsgCiAgZ2VvbV9saW5lKGFlcyh4ID0gY29uYywgeSA9IHVwdGFrZSkpCmBgYAoKVGhlIGNvZGUgYWJvdmUgZGlkIG5vdCB3b3JrLCBhbmQgdGhlIGxpbmUgZ3JhcGggaXMgbm90IGFwcHJvcHJpYXRlIGluIHRoaXMgY2FzZS4gVGhlcmUgYXJlIHNvIG1hbnkgYHVwZGF0ZWAgdmFsdWVzIGF0IHRoZSBzYW1lIGBjb25jYC4KCiMjIyBFeGFtcGxlLiBgZGF0YXNldHM6OlNlYXRiZWx0c2AKClNlYXJjaCB0aGUgZGF0YSBpbmZvcm1hdGlvbi4KCioqUm9hZCBDYXN1YWx0aWVzIGluIEdyZWF0IEJyaXRhaW4gMTk2OeKAkzg0KioKCiogU2VhdGJlbHRzIGlzIGEgbXVsdGlwbGUgdGltZSBzZXJpZXMsIHdpdGggY29sdW1ucwoqIERyaXZlcnNLaWxsZWQ6IGNhciBkcml2ZXJzIGtpbGxlZC4KKiBkcml2ZXJzOiBzYW1lIGFzIFVLRHJpdmVyRGVhdGhzLgoqIGZyb250OiBmcm9udC1zZWF0IHBhc3NlbmdlcnMga2lsbGVkIG9yIHNlcmlvdXNseSBpbmp1cmVkLgoqIHJlYXI6IHJlYXItc2VhdCBwYXNzZW5nZXJzIGtpbGxlZCBvciBzZXJpb3VzbHkgaW5qdXJlZC4KKiBrbXM6IGRpc3RhbmNlIGRyaXZlbi4KKiBQZXRyb2xQcmljZTogcGV0cm9sIHByaWNlLgoqIFZhbktpbGxlZDogbnVtYmVyIG9mIHZhbiAo4oCYbGlnaHQgZ29vZHMgdmVoaWNsZeKAmSkgZHJpdmVycy4KKiBsYXc6IDAvMTogd2FzIHRoZSBsYXcgaW4gZWZmZWN0IHRoYXQgbW9udGg/CgoqKlJlZmVyZW5jZXMqKgpIYXJ2ZXksIEEuIEMuIGFuZCBEdXJiaW4sIEouICgxOTg2KS4gVGhlIGVmZmVjdHMgb2Ygc2VhdCBiZWx0IGxlZ2lzbGF0aW9uIG9uIEJyaXRpc2ggcm9hZCBjYXN1YWx0aWVzOiBBIGNhc2Ugc3R1ZHkgaW4gc3RydWN0dXJhbCB0aW1lIHNlcmllcyBtb2RlbGxpbmcuIEpvdXJuYWwgb2YgdGhlIFJveWFsIFN0YXRpc3RpY2FsIFNvY2lldHkgc2VyaWVzIEEsIDE0OSwgMTg34oCTMjI3LiBkb2k6MTAuMjMwNy8yOTgxNTUzLgoKX1RoZSBwYXBlciBpcyBhdmFpbGFibGUgYXMgeW91IGxvZy1pbiB0byBJQ1UgTGlicmFyeSA+IEUtRGF0YWJhc2VzID4gSlNUT1JfCgpDYW4geW91IHNlZSB0aGUgZGlmZmVyZW5jZSBvZiB0aGUgZm9sbG93aW5nIHR3byBjb2Rlcz8KCmBgYHtyfQpoZWFkKFNlYXRiZWx0cykKYGBgCgoKYGBge3J9CmRmX3NiIDwtIGFzX3RpYmJsZShkYXRhc2V0czo6U2VhdGJlbHRzKQpkZl9zYgpgYGAKCmBgYHtyfQpzdW1tYXJ5KGRmX3NiKQpgYGAKCldoaWNoIHZpc3VhbGl6YXRpb24gZG8geW91IGFwcGx5PwoKYGBge3J9CmRmX3NiICU+JSBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IobGF3KSwgeSA9IERyaXZlcnNLaWxsZWQpKSArIGdlb21fYm94cGxvdCgpCmBgYAoKV2hhdCBkbyB5b3Ugb2JzZXJ2ZSBhYm92ZT8KCmBgYHtyfQpkZl9zYiAlPiUgZ2dwbG90KGFlcyh4ID0gUGV0cm9sUHJpY2UsIHkgPSBEcml2ZXJzS2lsbGVkKSkgKyBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKGZvcm11bGEgPSB5fngsIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpCmBgYAoKV2hhdCBjYW4geW91IHNlZSBhYm92ZT8KCmBgYHtyfQpkZl9zYiAlPiUgZ2dwbG90KGFlcyh4ID0ga21zLCB5ID0gRHJpdmVyc0tpbGxlZCkpICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChmb3JtdWxhID0geX54LCBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKQpgYGAKCldoYXQgY2FuIHlvdSBzZWUgYWJvdmU/CgpXZSB3aWxsIGxlYXJuIGhvdyB0byB1c2UgYHBpdm90X2xvbmdlcmAgYW5kIGBwaXZvdF93aWRlcmAgaW4gRURBNC4KCmBgYHtyfQpkZl9zYiAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAyOjQsIG5hbWVzX3RvID0gInNlYXQiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKQpgYGAKCmBgYHtyfQpkZl9zYiAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAyOjQsIG5hbWVzX3RvID0gInNlYXQiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUKICBnZ3Bsb3QoKSArIGdlb21fYm94cGxvdChhZXMoeCA9IHNlYXQsIHkgPSB2YWx1ZSwgZmlsbCA9IHNlYXQpKQpgYGAKCldoYXQgY2FuIHlvdSBvYnNlcnZlPwoKCgpgYGB7cn0KZGYgJT4lIGZpbHRlcihjb250aW5lbnQgPT0gIkFzaWEiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gZ2RwUGVyY2FwLCBjb2xvciA9IGNvdW50cnkpKSArIGdlb21fbGluZSgpCmBgYAoKQXBwcm9wcmlhdGUgZ3JhcGg/CgojIyBHYXBtaW5kZXIKCmBgYHtyfQpnZ3Bsb3QoZGYsIGFlcyh4ID0gY29udGluZW50KSkgKyBnZW9tX2JhcigpCmBgYAoKYGBge3J9CmdncGxvdChkZiwgYWVzKHggPSBjb250aW5lbnQsIGZpbGwgPSBjb250aW5lbnQpKSArIGdlb21fYmFyKHdpZHRoID0gMC43NSkKYGBgCgpgYGB7cn0KZ2dwbG90KGRmLCBhZXMoeCA9IGNvbnRpbmVudCwgeSA9IHBvcCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7cn0KZ2dwbG90KGRmLCBhZXMoeCA9IGNvbnRpbmVudCwgeSA9IGxvZzEwKHBvcCkpKSArIGdlb21fYm94cGxvdCgpCmBgYAoKQWx0ZXJuYXRlbHksIHlvdSBjYW4gdXNlIGBjb29yZF90cmFucyh4ID0gImlkZW50aXR5IiwgeSA9ICJsb2cxMCIpYCBpbiBzdGVhZCBvZiBgeSA9IGxvZzEwKHBvcClgLgpDYW4geW91IHNlZSB0aGUgZGlmZmVyZW5jZT8KCmBgYHtyfQpnZ3Bsb3QoZGYsIGFlcyh4ID0gY29udGluZW50LCB5ID0gcG9wKSkgKyBnZW9tX2JveHBsb3QoKSArIAogIGNvb3JkX3RyYW5zKHggPSAiaWRlbnRpdHkiLCB5ID0gImxvZzEwIikKYGBgCgpgYGB7cn0KZGYgJT4lIGZpbHRlcih5ZWFyICVpbiUgYygxOTU3LCAxOTgyLCAyMDA3KSkgJT4lCiAgZ2dwbG90KCkgKyBnZW9tX2JveHBsb3QoYWVzKHggPSBjb250aW5lbnQsIHkgPSBwb3AsIGZpbGwgPSBmYWN0b3IoeWVhcikpKSArCiAgICBjb29yZF90cmFucyh4ID0gImlkZW50aXR5IiwgeSA9ICJsb2cxMCIpCmBgYAoKCmBgYHtyfQpkZiAlPiUgZmlsdGVyKHllYXIgJWluJSBjKDE5NTcsIDE5ODIsIDIwMDcpKSAlPiUKICBnZ3Bsb3QoKSArIGdlb21fYm94cGxvdChhZXMoeCA9IGNvbnRpbmVudCwgeSA9IGdkcFBlcmNhcCwgZmlsbCA9IGZhY3Rvcih5ZWFyKSkpICsKICBjb29yZF90cmFucyh4ID0gImlkZW50aXR5IiwgeSA9ICJsb2cxMCIpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoZGYsIGFlcyhnZHBQZXJjYXAsIGxpZmVFeHApKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yPWNvbnRpbmVudCkpCmBgYAoKCmBgYHtyfQpkZiAlPiUgZ2dwbG90KGFlcyhnZHBQZXJjYXAsIGxpZmVFeHAsIGNvbG9yID0gY29udGluZW50KSkgKyBnZW9tX3BvaW50KCkgKwogIGNvb3JkX3RyYW5zKHggPSAibG9nMTAiLCB5ID0gImlkZW50aXR5IikKYGBgCgpgYGB7cn0KZGYgJT4lIGZpbHRlcih5ZWFyICVpbiUgYygyMDA3KSkgJT4lCiAgZ2dwbG90KCkgKyBnZW9tX3BvaW50KGFlcyh4ID0gcG9wLCB5ID0gZ2RwUGVyY2FwLCBjb2wgPSBjb250aW5lbnQpKSArCiAgY29vcmRfdHJhbnMoeCA9ICJsb2cxMCIsIHkgPSAibG9nMTAiKQpgYGAKYGBge3J9CmRmICU+JSBmaWx0ZXIoeWVhciAlaW4lIGMoMTk1NykpICU+JQogIGdncGxvdCgpICsgZ2VvbV9wb2ludChhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAsIGNvbCA9IGNvbnRpbmVudCwgc2l6ZSA9IHBvcCkpICsKICBjb29yZF90cmFucyh4ID0gImxvZzEwIiwgeSA9ICJpZGVudGl0eSIpCmBgYAoKYGBge3J9CmRmICU+JSBmaWx0ZXIoeWVhciAlaW4lIGMoMjAwNykpICU+JQogIGdncGxvdCgpICsgZ2VvbV9wb2ludChhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAsIGNvbCA9IGNvbnRpbmVudCwgc2l6ZSA9IHBvcCkpICsKICBjb29yZF90cmFucyh4ID0gImxvZzEwIiwgeSA9ICJpZGVudGl0eSIpCmBgYAoKYGBge3J9CmRmICU+JSBmaWx0ZXIoeWVhciAlaW4lIGMoMTk1NywgMjAwNykpICU+JQogIGdncGxvdCgpICsgZ2VvbV9wb2ludChhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAsIGNvbCA9IGNvbnRpbmVudCwgc2l6ZSA9IHBvcCkpICsKICBjb29yZF90cmFucyh4ID0gImxvZzEwIiwgeSA9ICJpZGVudGl0eSIpICsKICBmYWNldF93cmFwKHZhcnMoeWVhcikpCmBgYAoKCgpgYGB7cn0KZGZfbGlmZUV4cCA8LSBkZiAlPiUgZ3JvdXBfYnkoY29udGluZW50LCB5ZWFyKSAlPiUgCiAgc3VtbWFyaXplKG1lYW5fbGlmZUV4cCA9IG1lYW4obGlmZUV4cCksIG1lZGlhbl9saWZlRXhwID0gbWVkaWFuKGxpZmVFeHApLCBtYXhfbGlmZUV4cCA9IG1heChsaWZlRXhwKSwgbWluX2xpZmVFeHAgPSBtaW4obGlmZUV4cCkpCmBgYAoKVGhlIGNvZGUgYWJvdmUgZ2l2ZXMgYSBtZXNzYWdlLCBidXQgaXQgd29ya3MuCgpgYGB7cn0KZGZfbGlmZUV4cCAlPiUgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IG1lZGlhbl9saWZlRXhwLCBjb2xvciA9IGNvbnRpbmVudCkpICsKICBnZW9tX2xpbmUoKQpgYGAKCklmIHlvdSBkbyBub3Qgd2FudCB0byBoYXZlIGEgbWVzc2FnZSwgdGhlIGZvbGxvd2luZyBpcyBhbiBvcHRpb24uIE90aGVyd2lzZSwgZ3JvdXBpbmcgaXMga2VwdCBhbmQgeW91IGNhbiBnZXQgdGhlIG9yaWdpbmFsIGRhdGEgYmFjayBieSBgdW5ncm91cCgpYC4KCmBgYHtyfQpkZl9wb3AgPC0gZGYgJT4lIGdyb3VwX2J5KGNvbnRpbmVudCwgeWVhcikgJT4lIAogIHN1bW1hcml6ZShtZWFuX3BvcCA9IG1lYW4ocG9wKSwgbWVkaWFuX3BvcCA9IG1lZGlhbihwb3ApLCBtYXhfcG9wID0gbWF4KHBvcCksIG1pbl9wb3AgPSBtaW4ocG9wKSwgLmdyb3VwcyA9ICJkcm9wIikKYGBgCgpgYGB7cn0KZGZfcG9wICU+JSBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbWVhbl9wb3AsIGNvbG9yID0gY29udGluZW50KSkgKwogIGdlb21fbGluZSgpCmBgYApUaGUgZm9sbG93aW5nIHR3byBjb2RlcyBjcmVhdGUgdGhlIHNhbWUgY2hhcnQuCgpgYGB7cn0KZGZfcG9wICU+JSBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbWVhbl9wb3AsIGNvbG9yID0gY29udGluZW50LCBsaW5ldHlwZSA9IGNvbnRpbmVudCkpICsKICBnZW9tX2xpbmUoKQpgYGAKCmBgYHtyfQpkZl9wb3AgJT4lIGdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gbWVhbl9wb3AsIGNvbG9yID0gY29udGluZW50KSkgKyAKICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gbWVkaWFuX3BvcCwgbGluZXR5cGUgPSBjb250aW5lbnQpKQpgYGAKCgoKIyBSZXNwb25zZXMgdG8gUXVlc3Rpb25zCgojIyBRMS4gVHdvIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhbmQgb25lIG51bWVyaWNhbCB2YXJpYWJsZXMKCkVnLiBbU21va2luZywgQWxjb2hvbCBhbmQgKE8pZXNvcGhhZ2VhbCBDYW5jZXJdKGh0dHBzOi8vc3RhdC5ldGh6LmNoL1ItbWFudWFsL1ItZGV2ZWwvbGlicmFyeS9kYXRhc2V0cy9odG1sL2Vzb3BoLmh0bWwpCgpgYGB7cn0KKGRmX2Vzb3BoIDwtIGFzX3RpYmJsZShlc29waCkpCmBgYAoKYGRmX2Vzb3BoYCBoYXMgdGhyZWUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFuZCBvbmUgbnVtZXJpY2FsIHZhcmlhYmxlIGBuY2FzZXNgIHRvIGludmVzdGlnYXRlLgoKKipDb21tZW50cyoqOiBJIHdhbnRlZCB0byBpbmNsdWRlIHRocmVlIHZhcmlhYmxlcyBpbiB0aGUgZmlyc3QgZXhlcmNpc2UgdG8gYmUgYWJsZSB0byBjb21wYXJlIHRvYmFjY28gY29uc3VtcHRpb24sIG51bWJlciBvZiBjYXNlcyBvZiBjYW5jZXIsIGFuZCBhZ2UgaW4gdGhlIHNhbWUgZ3JhcGggYnV0IEkgd2FzIG5vdCBhYmxlIHRvIGRvIGl0LgoKKipTb2x1dGlvbnMqKjogVGhlcmUgYXJlIHZhcmlvdXMgd2F5cyB5b3UgY2FuIGNob29zZSBmcm9tLgoKU2NhdHRlciBwbG90IHdpdGggc2l6ZSBieSBgZ2VvbV9wb2ludCgpYC4KCmBgYHtyfQpnZ3Bsb3QoZGZfZXNvcGgpICsgZ2VvbV9wb2ludChhZXMoYWdlZ3AsIHRvYmdwLCBzaXplPW5jYXNlcykpCmBgYAoKSGVhdG1hcCB3aXRoIGBnZW9tX3RpbGUoKWAKCmBgYHtyfQpnZ3Bsb3QoZGZfZXNvcGgpICsgZ2VvbV90aWxlKG1hcHBpbmcgPSBhZXMoeCA9IGFnZWdwLCB5ID0gdG9iZ3AsIGZpbGwgPSBuY2FzZXMpKQpgYGAKCmBnZW9tX2JveHBsb3QoKWAKCmBgYHtyfQpnZ3Bsb3QoZGZfZXNvcGgsIGFlcyh4PSB0b2JncCwgeT1uY2FzZXMsIGZpbGw9YWdlZ3ApKStnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGZfZXNvcGgsIGFlcyh4PSBhZ2VncCwgeT1uY2FzZXMsIGZpbGw9dG9iZ3ApKStnZW9tX2JveHBsb3QoKQpgYGAKCmBnZW9tX2NvbCgpYAoKYGBge3J9CmdncGxvdChkZl9lc29waCwgYWVzKHg9IGFnZWdwLCB5PW5jYXNlcywgZmlsbD10b2JncCkpK2dlb21fY29sKCkKYGBgCgpEZWZhdWx0IHBvc2l0aW9uIGlzICJzdGFjayIuCgpgYGB7cn0KZ2dwbG90KGRmX2Vzb3BoLCBhZXMoeD0gYWdlZ3AsIHk9bmNhc2VzLCBmaWxsPXRvYmdwKSkrZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKCgojIyBRMi4gQ29tYmluZSB0d28gY2hhcnRzCgpgYGB7cn0KZGYgJT4lCiAgc2VsZWN0KGNvdW50cnksIGNvbnRpbmVudCwgeWVhciwgZ2RwUGVyY2FwKSAlPiUKICBmaWx0ZXIoY29udGluZW50ICVpbiUgYygiQXNpYSIsICJFdXJvcGUiKSkgJT4lCiAgZ3JvdXBfYnkoY29udGluZW50LCB5ZWFyKSAlPiUKICBzdW1tYXJpc2UobWVhbl9HRFBwZXJDYXBpdGEgPSBtZWFuKGdkcFBlcmNhcCkpICU+JQogIGdncGxvdChhZXMoeD15ZWFyKSkgKwogIGdlb21fbGluZShhZXMoeT1tZWFuX0dEUHBlckNhcGl0YSwgY29sb3I9Y29udGluZW50KSkgKwogIGdndGl0bGUoIkdEUCBvZXIgY2FwaXRhIGJ5IGNvbnRpbmVudHMsIDE5NTAncyB0byB0b2RheSIpIApgYGAKCgpgYGB7cn0KZGYgJT4lIAogIHNlbGVjdChjb3VudHJ5LCB5ZWFyLCBnZHBQZXJjYXApICU+JSAKICBmaWx0ZXIoY291bnRyeSAlaW4lIGMoIklzcmFlbCIsICJKYXBhbiIsICJOb3J3YXkiLCAiQ2hpbmEiLCAiSXJlbGFuZCIpKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXllYXIpKSArIAogIGdlb21fbGluZShhZXMoeT1nZHBQZXJjYXAsIGNvbG9yPWNvdW50cnkpKQpgYGAKCioqUXVlc3Rpb24uKiogSSBoYXZlIG5vdCBtYW5hZ2VkIHRvIGFkZCBvbiB0aGUgc2FtZSBncmFwaCBvZiB0aGUgY29udGluZW50cyB0aGUgZGF0YSBmb3IgdGhlIGluZGl2aWR1YWwgY291bnRyaWVzLCBhcyBJIHdvdWxkIGhhdmUgbGlrZWQ6CgoqKlNvbHV0aW9uLioqIENvbnN0cnVjdCB0d28gZGF0YSBzZXRzIGFuZCBjb21iaW5lIHRoZW0gaW50byBvbmUuCgpgZ2dwbG90MmAgc3RhcnRzIHdpdGggb25lIGRhdGEuIAoKYGBge3J9CmRmXzJjIDwtIGRmICU+JQogIHNlbGVjdChjb250aW5lbnQsIHllYXIsIGdkcFBlcmNhcCkgJT4lCiAgZmlsdGVyKGNvbnRpbmVudCAlaW4lIGMoIkFzaWEiLCAiRXVyb3BlIikpICU+JQogIGdyb3VwX2J5KGNvbnRpbmVudCwgeWVhcikgJT4lCiAgc3VtbWFyaXNlKGdkcFBlcmNhcCA9IG1lYW4oZ2RwUGVyY2FwKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lCiAgc2VsZWN0KGNvdW50cnkgPSBjb250aW5lbnQsIHllYXIsIGdkcFBlcmNhcCkKCmRmXzVjIDwtIGRmICU+JQogIHNlbGVjdChjb3VudHJ5LCB5ZWFyLCBnZHBQZXJjYXApICU+JQogIGZpbHRlcihjb3VudHJ5ICVpbiUgYygiSXNyYWVsIiwgIkphcGFuIiwgIk5vcndheSIsICJDaGluYSIsICJJcmVsYW5kIikpCgpkZl8yYyAlPiUgYmluZF9yb3dzKGRmXzVjKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gZ2RwUGVyY2FwLCBjb2xvciA9IGNvdW50cnkpKSArIGdlb21fbGluZSgpCmBgYAoKVXNlIGBtdXRhdGVgLgoKYGBge3J9CmRmICU+JQogIGdyb3VwX2J5KGNvbnRpbmVudCwgeWVhcikgJT4lCiAgbXV0YXRlKG1lYW5fYnlfY29udGluZW50ID0gbWVhbihnZHBQZXJjYXApKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZmlsdGVyKGNvdW50cnkgJWluJSBjKCJJc3JhZWwiLCAiSmFwYW4iLCAiTm9yd2F5IiwgIkNoaW5hIiwgIklyZWxhbmQiKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhcikpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGdkcFBlcmNhcCwgY29sb3I9Y291bnRyeSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IG1lYW5fYnlfY29udGluZW50LCBsaW5ldHlwZT1jb250aW5lbnQpKSArCiAgICBsYWJzKHRpdGxlID0gIkdEUCBvZXIgY2FwaXRhIG9mIGZpdmUgY291bnRyaWVzIiwgc3VidGl0bGUgPSAiTWVhbiBvZiBHRFAgcGVyIGNhcGl0YSBvZiB0aGVpciBjb250aW5lbnQiKSAKYGBgCgpXaGVuIHlvdSB3YW50IHRvIGNoYW5nZSB0aGUgbGluZXR5cGUgbWFudWFsbHksIHVzZSBgc2NhbGVfbGluZXR5cGVfbWFudWFsKClgLgoKYGBge3J9CmRmICU+JQogIGdyb3VwX2J5KGNvbnRpbmVudCwgeWVhcikgJT4lCiAgbXV0YXRlKG1lYW5fYnlfY29udGluZW50ID0gbWVhbihnZHBQZXJjYXApKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZmlsdGVyKGNvdW50cnkgJWluJSBjKCJJc3JhZWwiLCAiSmFwYW4iLCAiTm9yd2F5IiwgIkNoaW5hIiwgIklyZWxhbmQiKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhcikpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGdkcFBlcmNhcCwgY29sb3I9Y291bnRyeSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IG1lYW5fYnlfY29udGluZW50LCBsaW5ldHlwZT1jb250aW5lbnQpKSArIAogICAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMoIkFzaWEiID0gInR3b2Rhc2giLCAiRXVyb3BlIiA9ICJsb25nZGFzaCIpKSArIAogICAgbGFicyh0aXRsZSA9ICJHRFAgb2VyIGNhcGl0YSBvZiBmaXZlIGNvdW50cmllcyIsIHN1YnRpdGxlID0gIk1lYW4gb2YgR0RQIHBlciBjYXBpdGEgb2YgdGhlaXIgY29udGluZW50IikgCmBgYAoKIyBBcHBlbmRpeDogQ2hhbmdlIGNvbG9ycywgc2hhcGVzLCBsaW5ldHlwZXMsIGV0Yy4gbWFudWFsbHkKCkV4YW1wbGU6IERlZmF1bHQKCmBgYHtyfQpkZiAlPiUKICBmaWx0ZXIoY291bnRyeSAlaW4lIGMoIkdlcm1hbnkiLCAiSmFwYW4iLCAiVW5pdGVkIFN0YXRlcyIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gZ2RwUGVyY2FwLCBjb2xvcj1jb3VudHJ5LCBsaW5ldHlwZT1jb250aW5lbnQpKQpgYGAKCiogYHNjYWxlX2NvbG9yX21hbnVhbGA6IGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy9zY2FsZS1jb2xvdXIuaHRtbAogIC0gZWcxOiBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsICJibHVlIiwgImdyZWVuIikpCiAgLSBlZzI6IHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiQ2hpbmEiID0gInJlZCIsICJKYXBhbiIgPSAiYmx1ZSIsICJOb3J3YXkiID0gImdyZWVuIikpCiAgLSBlZzM6IHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gc2NhbGVzOjpodWVfcGFsKCkoMykpICMgZGVmYXVsdAogIC0gZWc0OiBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHNjYWxlczo6aHVlX3BhbChkaXJlY3Rpb24gPSAtMSkoMykpICMgcmV2ZXJzZSBvcmRlcgoqIGBzY2FsZV9maWxsX21hbnVhbGA6IHNpbWlsYXIgdG8gIGBzY2FsZV9jb2xvcl9tYW51YWxgCiogYHNjYWxlX2xpbmV0eXBlX21hbnVhbGA6IGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy9zY2FsZS1vdGhlci5odG1sP3E9bGluZXR5cGUjc2NhbGUtbGluZXR5cGUKKiBgc2NhbGVfc2hhcGVfbWFudWFsOiBodHRwczovL2dncGxvdDItYm9vay5vcmcvc2NhbGUtb3RoZXIuaHRtbD9xPXNjYWxlX3NoYXBlX21hbnVhbCNzY2FsZS1zaGFwZQoqIGBzY2FsZV9zaXplOiBodHRwczovL2dncGxvdDItYm9vay5vcmcvc2NhbGUtb3RoZXIuaHRtbD9xPXNpemUjc2NhbGUtc2l6ZQoKCmBgYHtyfQpkZiAlPiUKICBmaWx0ZXIoY291bnRyeSAlaW4lIGMoIkdlcm1hbnkiLCAiSmFwYW4iLCAiVW5pdGVkIFN0YXRlcyIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gZ2RwUGVyY2FwKSkgKwogICAgZ2VvbV9saW5lKGFlcyhjb2xvcj1jb3VudHJ5LCBsaW5ldHlwZT1jb250aW5lbnQpKSArCiAgICBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGNvdW50cnkpKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHNjYWxlczo6aHVlX3BhbChkaXJlY3Rpb24gPSAtMSkoMykpICsKICAgIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKCJFdXJvcGUiID0gImRvdHRlZCIsICJBc2lhIiA9ICJkb3RkYXNoIiwgIkFtZXJpY2FzIiA9ICJsb25nZGFzaCIpKSArCiAgICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygiR2VybWFueSIgPSA3LCAiSmFwYW4iID0gOSwgIlVuaXRlZCBTdGF0ZXMiID0gMTIpKQpgYGA=